Adopt the Sonar/Checkstyle/FindBugs ruff rule-family bar; fix the src/ baseline - #102
Conversation
Turn on C90/N/PLR/PLW/PLC/ERA/T20/ARG/RET/SIM/PIE/FBT/A/B/S/BLE/TRY/RUF in [tool.ruff.lint] select per plan/coding-standards.md §3, with mccabe max-complexity=10 and pylint max-args=5/max-branches=12/ max-returns=6/max-statements=50. tests/ and acceptance/ get a documented per-file-ignore for S101 (bare assert by design), FBT (fixture values as positional args) and PLR2004 (literal test data, not magic values). The baseline this select turns up is fixed category by category in the commits that follow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
RUF100 (noqa comments no longer needed once the new rule set proved them unnecessary -- ruff confirmed F401/BLE001 don't fire on the underlying lines), RUF022 (__all__ sort order), PLC0207 (str.split needs maxsplit), PLR0402 (manual from-import for importlib.resources), SIM300 (yoda-condition reorder). All via targeted `ruff check --select <rule> --fix`, reviewed individually, never a blanket --fix. Full suite green (1036 passed, 6 skipped). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
Replace try/except-pass patterns with contextlib.suppress(...), and narrow to OSError where the guarded call (chmod, rmdir) only ever raises that (was a broad `except Exception` in two installers.py spots). zip() over paired same-length tuples in toggles.py now declares strict=True. Full suite green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
…gged The prior commit ran `ruff check --select RUF100,... --fix`, and a CLI --select overrides the project's configured select entirely for that invocation -- so RUF100 evaluated "unused" against a rule set of just those five codes, not the full configured bar, and treated every noqa:F401/noqa:BLE001 as unused because F401/BLE001 weren't even being checked in that run. Restores the six genuinely-needed noqa comments (facade re-export F401s in registry/__init__.py and validation/__init__.py, and BLE001 on three deliberate best-effort except-Exception blocks in autocompile.py and scaffold/recompile.py) and keeps the two RUF100 removals that verify as genuinely unused under the full project config (hooks/install.py's F401, covered by an __all__; scaffold/recompile.py's BLE001 on a block that re-raises, which BLE001 doesn't flag as blind). Verified against `ruff check src/` with no CLI --select override. Full suite green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaced bare int/word-count comparisons (row/path segment counts, gate exit codes, name/paragraph/line-length/staleness thresholds) with named module-level constants documenting what the number means, across authoring/matrix.py, eval/execute.py, the vendored gate runner, manifest.py, plugin/marketplace.py, validation/checks_content.py and validation/frontier.py. Resynced the vendored runtime copy. Full suite green. Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New tools/check_literal_duplication.py: an AST-based Sonar S1192-style check flagging any non-docstring string constant >= 20 chars that repeats >= 3 times outside data/templates/tests, with tools/literal_duplication_allowlist.json as the escape hatch (every entry must carry a non-empty reason -- enforced by load_allowlist()). tests/test_literal_duplication.py covers the check's own behavior and asserts src/ is clean under it. Fixed every literal the check found on src/ by extracting a shared constant or hoisting a repeated block into a function, rather than allowlisting: BUDGETS-key lookups cached to a local once per function (checks_evals.py, checks_content.py); manifest.schema.json, content_instructions, determinization_reviewed and agent_specific_vocabulary centralized in manifest.py/loading.py and imported where checked; three emitters' identical package_data_dir call hoisted to one shared DATA_DIR in the package init; three near-identical scan-registry-and-report blocks in scaffold/init.py, new.py and skills.py replaced by one chock.registry.core.rescan_and_report(); the '--agents requires...' CLI message centralized in compile/surfaces.py; gate/schema.py's four schema objects share one _CLOSED_OBJECT fragment; dependency_allowlist/forbidden_path_regex deduplicated between gate/schema.py and eval/derive.py, leaving the vendored gate runner's own copy as the one deliberate exception (it cannot import its framework-side siblings and stay stdlib-only). No allowlist entries were needed -- everything the check found had a natural single owner once written that way. Resynced the vendored gate runtime after editing its source. Full suite green (1045 passed, 6 skipped); chock check and sync --check both pass. Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
37 raise-vanilla-args findings across 17 files, all the same shape -- a long f-string or literal built inline in the raise call. Extracted each to a 'msg = ...' assignment immediately before the raise, which keeps the exact same exception type and message while satisfying the rule (message construction is no longer inline at the raise site). No new exception classes were needed; every site already raised a purpose-built or stdlib exception type. Also fixed a stray I001 import-sort left over from an earlier commit, and trimmed hooks/in_agent_install.py by one line (a redundant early return the TRY003 fix pushed past the file's own 300-line budget -- the set comprehension already returns empty when nothing is installed, so the guard added nothing). Full suite green (1045 passed, 6 skipped); chock check and sync --check both pass; literal-duplication check still clean. Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three best-effort logging/fetch sites (guard_runner.py, gate runner's own outcome logger and dependency extractor, frontier_ingest.py's fetch_url) keep their broad except on purpose -- their own docstrings already say 'best effort, never raises' -- so each gets a noqa: BLE001 naming that contract. validation/frontier.py's except-Exception-pass narrows to (ValueError, TypeError), the actual failure modes of datetime.fromisoformat on a malformed timestamp; that also clears S110, which only flags a bare/blind except-pass. Resynced the vendored gate runtime. Full suite green (1045 passed, 6 skipped); chock check, sync --check and the literal-duplication check all pass. Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every boolean-typed function parameter across src/ (disabled, verbose, blocked, has_extended, enforced, allow_empty, force, agent_agnostic, skip_hooks, use_network, use_json) moves after a bare '*' so it can only be passed by keyword; every call site updated to match, including tests/ and the vendored gate runtime (guard_runner.py's log_outcome, composed into the runtime bundle -- goldens regenerated with CHOCK_REGEN_GOLDENS=1 and the repo's own vendored copies resynced). The handful of FBT003 call-site findings were Grade(..., False) NamedTuple constructions; the 'witnessed' field is now passed by keyword there too. Full suite green (1045 passed, 6 skipped); chock check, sync --check and the literal-duplication check all pass. Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Most findings are shared dispatch-table signatures (validation check_*(artifact_dir, manifest, artifact_type, report), emitter emit(policy_dir, output_dir, manifest), gate _kind_*(ctx, params, event)) where a given implementation legitimately ignores an argument every sibling in the table receives; each is prefixed with '_' (all call sites are positional, so this is a no-op at the call site). Same treatment for installers.install_validate_hook's repo_root, gateway/gates.evaluate's tool_name, and registry/core's extract_dependencies artifact param. lock.py's build_lock(repo_root, source_root=None) is different: source_root is dead -- no caller anywhere passes it, it is not exported or documented as public API, and the function ignores it entirely -- so it is deleted rather than prefixed, per the repo's own YAGNI rule. Regenerated goldens/resynced after gate/runner.py's two _kind_* renames. Full suite green (1045 passed, 6 skipped); chock check, sync --check and the literal-duplication check all pass. Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
All five are import-time or type-narrowing invariants over internal state or build-time constants -- never over untrusted request input -- so a noqa is the right tool, not a raise rewrite that would lose the type-checker narrowing (proxy.py, plugin/cli.py) or add ceremony around a same-module sanity check (in_agent.py, plugin/copilot.py, plugin/store.py). Each noqa names why. Full suite green. Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PLW1510: every subprocess.run() that already branches on
proc.returncode gets an explicit check=False -- the current implicit
default, just stated.
S607: every literal "git" argv resolved once per module via
shutil.which("git") or "git" (falls back to the bare name if
resolution fails, same as today). Since ruff can no longer see a
literal partial path, this also flips the finding to S603 at several
sites; each of those, plus every subprocess call whose whole job is
running a configured or user-invoked command (a gateway's downstream
MCP server, a guard script under test, a repo-registered review
check, git-catalog fetch for chock add), gets a noqa: S603 naming
why running a subprocess is the feature there, not a hardening gap.
The vendored gate runtime's guard_runner.py and sessionstart.py both
gained a `shutil` reference; runtime_bundle.py's compositor only
carries a fixed, explicit import/rename set into the bundle (see its
_RENAME dict and data/imports.py.tmpl), so shutil needed adding to
both -- caught by test_pretooluse*/test_sessionstart_arm* failing
with 'NameError: shutil' inside the composed runtime, not by ruff.
Goldens regenerated, repo resynced.
Full suite green (1045 passed, 6 skipped); chock check, sync --check
and the literal-duplication check all pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…rts lazy Of the 71 findings, 65 moved to module top level after verifying no circular import exists (checked by grepping each target module for a back-reference, then confirming with a fresh `python -c "import chock.<module>"` per file plus a full `import chock.cli` sanity check that exercises the CLI's own dynamic dispatch). Two genuine import cycles turned up this way and stayed lazy, each with a one-line comment plus inline noqa naming the cycle: validation/checks_drift.py's compiled_differences (-> scaffold.recompile -> registry.core -> registry.cli -> validation.report -> validation package init -> checks_drift, a cycle) and index/builder.py's discover_artifacts (-> validation.loading -> validation package init -> checks_repo -> index.builder, a cycle). Two more went back to lazy after the full suite caught it, not ruff: scaffold/recompile.py's build_lock/write_lock and hooks/autocompile.py's compile_policy are patched in tests (test_adopter_safety.py, test_engine_scan.py, test_agent_selection.py) via monkeypatch.setattr(source_module, "name", fake) -- a pattern that only works if the caller re-imports the name fresh on every call rather than binding it once at module load. Each site keeps its lazy import with a comment explaining why and an inline noqa. lifecycle.py and scaffold/remove.py stay fully lazy (per-file-ignore in pyproject.toml) for the same reason at file scale: tests/test_lifecycle.py and tests/test_remove.py patch nine different source modules this way across their test cases -- rewriting all of them to target the new binding location would be a bigger, riskier change than the lint finding warrants. One more real bug this surfaced: the vendored gate runtime's sessionstart.py needed importlib.util.find_spec, a dotted attribute chain the bundle compositor's Name-only renamer (runtime_bundle.py) can't rewrite -- attempting to hoist it produced a NameError inside the composed runtime, caught by test_sessionstart_arm.py, not ruff. Left lazy with a comment; guard_runner.py's json import and gate/build.py's chock.manifest import hoisted cleanly since neither needed bundle-compositor support (json ships in the bundle's fixed top-import set already; gate/build.py isn't vendored at all). Full suite green (1045 passed, 6 skipped); chock check, sync --check and the literal-duplication check all pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
New chock/output.py: warn(message) and error(message), each owning the "[WARN] "/"[ERROR] " prefix and the file=sys.stderr routing in one place. Migrated the 31 call sites across 16 files that matched the convention exactly (print(f"[WARN] ...", file=sys.stderr) or the [ERROR] equivalent) -- every one verified byte-identical (the helper emits the exact same prefix and stream). Three near-matches were left alone because they print to stdout, not stderr, and routing them through warn()/error() would have silently changed which stream the message lands on: registry/cli.py's finding line, registry/core.py's two manifest-scan messages, and scaffold/init.py's compound two-line [WARN] message. The other ~183 print() calls stay as direct calls: they render final CLI output (status lines, tables, "chock plugin build" summaries) in argparse main()/*_main() functions across chock's *cli.py, install*, scaffold/*, authoring/*, registry/*, plugin/* and validation/report.py -- every file T201 flags turns out to be a genuine CLI/render surface, which the next commit documents with per-file-ignores rather than wrapping 183 call sites in indirection that changes nothing about what gets printed. Full suite green (1045 passed, 6 skipped); chock check, sync --check and the literal-duplication check all pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
Every one of the 183 remaining print() call sites lives in a file that either dispatches an argparse subcommand (*_main()/main() across cli.py, lifecycle.py, toggles.py, */cli.py, hooks/install.py, scaffold/*, eval/cli.py), renders a report (validation/report.py, gatelog.py), or is chock's own low-level installer/hook logic printing operator-facing status. chock has no other consumer of these strings -- there is no library layer separate from the CLI here, so wrapping each call in an indirection layer would only add churn, not change behaviour or add a real seam. Each per-file-ignore entry names this once for the group, plus a one-off for the `chock new` skill script template stub, whose print() is placeholder scaffold content, not chock's own output. lifecycle.py and scaffold/remove.py already carried a PLC0415 per-file-ignore; merged T201 into the same two entries rather than duplicating the TOML key. Full suite green (1045 passed, 6 skipped); chock check, sync --check and the literal-duplication check all pass. Remaining ruff findings: 37, all C901/PLR091x complexity. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
37 findings across 21 functions in 21 files (some functions trip several of C901/PLR0911/PLR0912/PLR0913/PLR0915 at once). Every one needs an actual function split or an argument-bundling refactor to fix -- not a mechanical rewrite like every other category in this wave -- and the brief calls that out explicitly as its own behaviour-preservation-checked step, separate from turning the rule set on and clearing what's safely automatable. Attempting 21 such splits under this same pass risked exactly the kind of subtle behaviour drift (a reordered branch, a changed early-return, a moved side effect) the brief's gates exist to catch, for marginal lint benefit -- most of these are barely over the line (11-16 against a threshold of 10 or 12). Used the brief's own escape hatch for an oversized baseline: each per-file-ignore entry is scoped to only the specific rule codes that file needs (not a blanket ignore), marked TODO(lint-adoption), and comments the offending function name(s) so the entry is self-documenting. Full enumeration with file:line detail also goes in the w50 report on org-plan. `ruff check src/` is otherwise fully clean: every other finding from the measured baseline (503 after re-measuring against this branch's starting point, versus the brief's 525) is fixed, not deferred. tests/, acceptance/ and tools/ still carry ~423 findings under the new rule set beyond their existing S101/FBT/PLR2004 carve-out -- out of scope here: the brief's "fix the baseline" mandate and its own measured count (525 / re-measured 503) are both specifically about src/, never the test suite. Full suite green (1045 passed, 6 skipped); chock check, sync --check and the literal-duplication check all pass; `ruff check src/` clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
…ed_text Compact, short-code-english block naming the rule families in select (C90/N/PLR/PLW/PLC/ERA/T20/ARG/RET/SIM/PIE/FBT/A/B/S/BLE/TRY/RUF), the numeric thresholds, the magic-value remedy and its own duplication check, the boolean-keyword-only rule, and the per-file-ignore discipline (a reason always, TODO(lint-adoption) as the sanctioned escape hatch for an oversized baseline slice) -- matching the format the owner's coding-standards doc already states. Full suite green (1055 passed, 2 skipped -- more of the suite runs now that the build package is installed in this environment, unlocking wheel tests that were skipping for its absence); chock check, sync --check and the literal-duplication check all pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CI's validate job runs `ruff check .` across the whole repo, not just src/ (which is all this PR's W50 baseline sweep measured and fixed). Widening [tool.ruff.lint] select repo-wide therefore turned pre-existing, untouched findings under tests/, acceptance/, docs/assets/, and fuzz/ into new failures on every validate job (3 Python versions). Extend the existing tests/**/acceptance/** per-file-ignores and add docs/assets/** and fuzz/** entries covering exactly the rule codes that fire there today, marked TODO(lint-adoption) and enumerated by rule family in the org-plan report, consistent with the brief's per-file-ignore escape hatch for baseline too large for one PR. Drop one now-unused S602 noqa in tests/test_copilot_hook_runtime.py made redundant by the new tests/** ignore. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
…Error rename The N818 (error-suffix naming) fix earlier in this branch renamed chock.policy_id.InvalidPolicyId to InvalidPolicyIdError, but fuzz/ is outside src/ so it wasn't caught by the same-tree grep at the time. fuzz/fuzz_policy_id.py still imported and caught the old name, so every fuzz CI run failed at import with ModuleNotFoundError-adjacent ImportError before a single input ran. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
…y except Two "high" alerts (py/clear-text-logging-sensitive-data) fired on output.py's warn()/error() -- CodeQL's name-based heuristic, not an actual credential flow. Audited every call site in src/: each passes a diagnostic string (an exception message, a file path, or an INDEX token-budget count). The heuristic's likely trigger is call sites like index/render.py's max_tokens/main_tokens (LLM context-window counts, not auth tokens) and compile/emitters/in_agent.py's PROJECT_DIR_TOKEN, a literal shell placeholder string already ruled non-sensitive at its own definition (bandit S105). Documented the audit at the sink and added the standard lgtm[query-id] suppression comment GitHub code scanning honors for a reviewed false positive. Also gave the "note"-severity empty except in validation/frontier.py (a malformed/missing fetched_at timestamp, already deliberately swallowed) its explanatory comment. Neither change alters behaviour: comments only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
…ment why CI pins ruff>=0.16,<0.17 (pyproject.toml); the previous commit's local verification ran against a stale 0.15.8 shadowing the pip-installed 0.16.5 on PATH. 0.16 added PLR0917 (too-many-positional-arguments), which 0.15.8 never fires -- 10 findings appeared on re-scan with the correct binary, split across already-deferred src/ complexity findings (eval/derive.py's _case, gate/runner.py's run, plugin/store.py's three builders -- added to their existing per-file-ignore entries) and the same out-of-scope trees from the previous commit (tests/**, docs/assets/**). Also corrected eval/derive.py's stale ignore comment: the deferred PLR0913 function is _case, not build_case. The `lgtm[py/clear-text-logging-sensitive-data]` suppression comments added to output.py did not clear CodeQL's alerts on re-scan (new alerts 125/126 opened at the shifted line numbers; 122/123 just went outdated, not resolved) -- documented that finding instead of repeating an attempt already shown not to work, with the concrete traced source (index/render.py's token-BUDGET counts, not credentials) and what dismissing it needs that this session doesn't have. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
|
All CI-blocking findings from the first push are fixed and pushed except one, which needs a maintainer action: Fixed (commits
Not fixed — needs a maintainer with Security-tab access: CodeQL's Everything else (validate x3, fuzz) should be green on the current head ( Generated by Claude Code |
…t.py The lgtm[...] suppression the prior commit tried is the legacy LGTM.com syntax; GitHub code scanning's own alert-suppression directive is # codeql[rule-id] on the line immediately above the flagged statement (the same pattern already used in gate/sessionstart.py, chock#83). Applies it to both flagged prints in output.py (py/clear-text-logging-sensitive-data -- a name-based false positive on token-named diagnostic strings that are LLM context-window counts, not credentials; every warn()/error() call site in src/ was already audited and none carries credential material). The suppression comment reads as commented-out code to ruff's new ERA001 (this wave's own rule), so it carries its own justified noqa alongside it, matching this file's existing per-line noqa convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
…1 goes file-scoped instead The previous commit's # codeql[rule-id] # noqa: ERA001 -- text on one line did not clear the alert on re-scan (still flagged, same 2 alerts, confirmed against the new head). CodeQL's AlertSuppression matcher for py/clear-text- logging-sensitive-data most plausibly requires the comment line to end at the suppression directive -- trailing text (even as a second # comment) breaks the match. Dropped the trailing noqa; ERA001 is file-scoped in pyproject.toml instead (output.py is two functions, each with a real codeql[...] suppression comment above its print -- there is no other commented-out code in this file for ERA001 to actually catch). T201 rides the same file-scoped ignore, dropping the now-redundant per-line noqa. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
|
|
||
| def warn(message: str) -> None: | ||
| # codeql[py/clear-text-logging-sensitive-data] | ||
| print(f"[WARN] {message}", file=sys.stderr) |
|
|
||
| def error(message: str) -> None: | ||
| # codeql[py/clear-text-logging-sensitive-data] | ||
| print(f"[ERROR] {message}", file=sys.stderr) |
|
Status: CodeQL red, blocked on Security-tab access — needs the owner. All 16 other checks are green on this PR (currently head Two independent suppression attempts, both fully re-verified (full suite 1045/6,
Both re-scans still report the alerts as "New alert" at the (shifted) print lines. This suggests inline alert-suppression may not be enabled for this repository at the GitHub Advanced Security config level — something this session has no way to check or change. Leaving this in draft rather than guessing a third time. Needs one of: dismissing the alert by hand in the Security tab as a false positive, or confirming inline suppression is enabled so the syntax can be re-attempted with real UI feedback. Full detail in 🤖 Generated with Claude Code Generated by Claude Code Generated by Claude Code |
What
Adopts the owner's static-analysis standard (
plan/coding-standards.md§3): turns onC90 N PLR PLW PLC ERA T20 ARG RET SIM PIE FBT A B S BLE TRY RUFin[tool.ruff.lint] select(mccabe max-complexity 10, pylint max-args 5 / max-branches 12 / max-returns 6 / max-statements 50),
and fixes the resulting
src/baseline (re-measured at 503 findings on this branch's startingpoint) category by category, in bisectable commits, never a blanket
--fix. Adds a newtools/check_literal_duplication.py(+ test) implementing the Sonar S1192 "magic stringrepeated" check the brief calls for, and records the standard in
AGENTS.md's hard rulesbeside
code_comments/externalized_text.ruff check src/is fully clean except one deliberately deferred category (see below).Category-by-category summary (see individual commits for detail)
RUF100/022,SIM105,B905,PLC0207,PLR0402,SIM300, etc.):contextlib.suppress, explicitzip(strict=), stale-noqa cleanup — one commit fixed aself-inflicted mistake in the very next commit (see "Correct the previous commit" — a
ruff check --select X --fixinvocation implicitly narrows the active rule set toX,which made
RUF100wrongly call several still-needednoqacomments unused).PLR2004): named module constants for every bare comparison.shared constant or a repeated block into one function — no allowlist entries were needed.
TRY003,BLE001,S110): messages extracted to a local beforeraise; remaining broadexcept Exceptionsites either narrowed ornoqa-justified asdeliberate best-effort logging/fetch contracts.
FBT001/002/003): every boolean parameter moved after*(keyword-only);every call site updated, including the vendored gate runtime (goldens regenerated).
ARG001): interface-conformance params prefixed_; one genuinelydead parameter deleted per the repo's YAGNI rule.
S101): all five are internal invariants over trusted state, not requestvalidation —
noqa-justified rather than rewritten.PLW1510,S603,S607): explicitcheck=Falsewhere alreadyimplicit; every literal
"git"resolved viashutil.which; remainingS603sitesnoqa-justified where running a subprocess is the feature itself.PLC0415): 65 of 71 hoisted to module top after verifying no circularimport (a fresh
python -c "import chock.<module>"per file); the rest stayed lazy for areal reason each documents — two genuine import cycles, two sites the test suite patches via
monkeypatch.setattr(source_module, "name", fake)(only works with a fresh per-call import),and
lifecycle.py/scaffold/remove.pyat file scope for the same reason across ninepatched call sites in
tests/test_lifecycle.pyandtests/test_remove.py.T201): newchock/output.py(warn/error) absorbs the 31call sites that matched the
[WARN]/[ERROR]-to-stderr convention exactly; the other ~183are per-file-ignored as genuine CLI/render output — chock is its CLI, every flagged file is
an argparse
*_main()/main()dispatcher or a report renderer, and there is no separateconsumer of these strings to route through an indirection layer.
Deferred: complexity (
C901/PLR091x)37 findings across 21 functions need an actual split or argument-bundling refactor, which
the brief calls out as its own careful, behaviour-preservation-checked step — not attempted
in this same pass. Deferred via scoped
per-file-ignoresmarkedTODO(lint-adoption)(never a blanket ignore), each naming the offending function; full enumeration in the w50
report (
plan/spine-a/reports/w50.md, org-plan).tests/,acceptance/andtools/still carry ~423 findings under the new rule set beyondtheir existing
S101/FBT/PLR2004carve-out — out of scope: the brief's "fix the baseline"mandate and its measured count are both specifically about
src/.Behaviour preservation
chock check,chock check --only matrix,chock check --only verify,chock sync --repo . --check,ruff check .,ruff format --check .all clean.pytest acceptance/(21) andpytest acceptance/test_isolation.py(2) both green..chock/compiled/,coverage.json,chock.lock,INDEX.md, all vs. the branch point) is byte-identical everywhere except the vendored.chock/bin/*runtimes, whose source (gate/runner.py,guard_runner.py,sessionstart.py) this PR intentionally edited — every one of those diffs traces to aspecific, documented fix (keyword-only params, resolved git path, hoisted constants);
goldens regenerated with
CHOCK_REGEN_GOLDENS=1and committed.Definition of done
chock check→ 0 errors, 0 warnings, 0 infoschock check --only matrixpasses (no behavior change)chock sync --repo . --checkcleanchock check --only verifycleanpytest -qgreenpytest acceptance/greenruff check .andruff format --check .clean (on the covered scope; see "Deferred" above)Claims
is emitted or installed;
INSTALLED_SURFACES, the coverage table anddocs/enforcement-surfaces.mdare all unaffected.🤖 Generated with Claude Code
Generated by Claude Code